#include <cstdio>
#include <cstdlib>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <boost/algorithm/string/case_conv.hpp>

#define SERVER "irc.freenode.net"
#define PORT 6667
#define CHANNEL "#DeadIRC"
#define NICK "set_ya_nickname"
#define USER "set_ya_nickname"
#define NAME "DeadIRC"
#define PASS "" // user authentication password


int access_cnt = 1;
char* access_arr[] = { "KrzyStar" };


int irc_sock;
FILE* dataout;

struct irc_reply_data
{
	char* nick;
	char* ident;
	char* host;
	char* target;
};

int IRC_has_access(char *nickname)
{
	int i;
	for (i=0;i<access_cnt;i++)
	{
		if(access_arr[i]==nickname)
			return 1;
	}
	return 0;
}

int IRC_connect()
{
	hostent* resolv; // socket stuff START :)
	sockaddr_in rem;
	irc_sock=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (irc_sock==-1)
		return 1;
	resolv=gethostbyname(SERVER);
	if (!resolv)
	{
		close(irc_sock);
		return 1;
	}
	memcpy(&rem.sin_addr, resolv->h_addr, 4);
	rem.sin_family=AF_INET;
	rem.sin_port=htons(PORT);
	if (connect(irc_sock, (const sockaddr*)&rem, sizeof(rem))==-1)
	{
		close(irc_sock);
		return 1;
	}
	dataout=fdopen(irc_sock, "w");
	if (!dataout)
	{
		printf("Failed to open streams!\n");
		close(irc_sock);
		return 1;
	} // socket stuff END
	fprintf(dataout, "NICK %s\r\n", NICK); // send nick/user/name to the server
	fprintf(dataout, "USER %s * 0 :%s\r\n", USER, NAME);
	fflush(dataout);
	return 0;
}

void IRC_set_mode(char *mode_data,char *msg,char *origin)
{
	if ( strlen(msg)<2 )
		fprintf(dataout, "MODE %s %s %s\r\n", CHANNEL, mode_data, origin);
	else
		fprintf(dataout, "MODE %s %s %s\r\n", CHANNEL, mode_data, msg+1);
	fflush(dataout);
}

void IRC_send_message(char *data)
{
	if ( strlen(data)>0 )
		fprintf(dataout, "PRIVMSG %s :%s\r\n", CHANNEL, data);
	fflush(dataout);
}

void IRC_kick(char *msg,char *origin)
{
	if ( strlen(msg)<2 )
		fprintf(dataout, "KICK %s %s\r\n", CHANNEL, origin);
	else
		fprintf(dataout, "KICK %s %s\r\n", CHANNEL, msg+1);
	fflush(dataout);
}

int IRC_self_prot(char *msg)
{
	char msg_prot[512], nick_prot[512];
	strcpy(msg_prot,msg);
	strcpy(nick_prot,NICK);
	boost::to_upper(msg_prot);
	boost::to_upper(nick_prot);
	if (strstr(msg_prot,nick_prot))
		return 1;
	else
		return 0;
}


void IRC_action(char* hostd,char* cmd,char* params,irc_reply_data hostd_tmp)
{
	printf("%s -- %s\n",params,cmd); // debug part -- prints out data from sockets
	if ( !strcmp(cmd, "376") ) // wait for "end of /MOTD"
	{
		fprintf(dataout, "PRIVMSG NickServ :IDENTIFY %s\r\n", PASS); // identify nickname
		fprintf(dataout, "JOIN %s\r\n", CHANNEL); // join the specified channel
		fflush(dataout);
	}
	if ( !strcmp(cmd, "KICK") ) // AutoRejoin part -- if you don't want it just comment it out
	{
		char kickstr[512];
		char *kickstr_p;
		kickstr_p = kickstr;
		strncpy(kickstr_p,CHANNEL,strlen(CHANNEL));
		kickstr_p += strlen(CHANNEL);
		strncpy(kickstr_p," ",1);
		kickstr_p++;
		strncpy(kickstr_p,NICK,strlen(NICK));
		kickstr_p += strlen(NICK);
		strncpy(kickstr_p," :",2);
		kickstr_p += 2;
		strncpy(kickstr_p,NICK,strlen(NICK));
		kickstr_p += strlen(NICK);
		strncpy(kickstr_p,"\00",1); // yeah, i know i could do this better :)
		if ( !strcmp(kickstr, params) )
		{
			fprintf(dataout, "JOIN %s\r\n", CHANNEL);
			fflush(dataout);
		}
	}
	if ( !strcmp(cmd, "PRIVMSG") )
	{
		if ( !strncmp(params,CHANNEL,strlen(CHANNEL)) ) // channel event parsing START
		{
			char msg[512]; // message buffer
			strcpy(msg, params+strlen(CHANNEL)+2);
			if ( strstr(msg, "xD") ) // "xD"kicker -- still case sensitive
			{
				IRC_kick(hostd_tmp.nick-1, hostd_tmp.nick);
			}
			if ( IRC_self_prot(msg)==1 )
				return;
			if ( !strncmp(msg, "!nick ",6) ) // bot's nickname change
			{
				fprintf(dataout, "NICK %s\r\n", msg+6);
				fflush(dataout);
			}
			if ( !strncmp(msg, "!op",3) ) // grant +o status
				{IRC_set_mode("+o",msg+3,hostd_tmp.nick); printf("op!\n");}
			if ( !strncmp(msg, "!deop",5) ) // remove +o status
				IRC_set_mode("-o",msg+5,hostd_tmp.nick);
			if ( !strncmp(msg, "!voice",6) ) // voice user
				IRC_set_mode("+v",msg+6,hostd_tmp.nick);
			if ( !strncmp(msg, "!devoice",8) ) // devoice user
				IRC_set_mode("-v",msg+8,hostd_tmp.nick);
			if ( !strncmp(msg, "!ban",4) ) // ban user/hostmask
				IRC_set_mode("+b",msg+4,hostd_tmp.nick);
			if ( !strncmp(msg, "!unban",6) ) // unban user/hostmask
				IRC_set_mode("-b",msg+6,hostd_tmp.nick);
			if ( !strncmp(msg, "!kick",5) ) // kick user
				IRC_kick(msg+5,hostd_tmp.nick);
			if ( !strncmp(msg, "!kb",3) ) // kick 'n ban :)
			{
				IRC_kick(msg+3,hostd_tmp.nick);
				IRC_set_mode("+b",msg+3,hostd_tmp.nick);
			}
			if ( !strncmp(msg, "!bk",3) ) // ban 'n kick :D
			{
				IRC_set_mode("+b",msg+3,hostd_tmp.nick);
				IRC_kick(msg+3,hostd_tmp.nick);
			}
			if ( !strncmp(msg, "!version",8) ) // display version info
			{
				IRC_send_message("DeadIRC v0.3 by KrzyStar");
			}
			if ( !strncmp(msg, "!whoami",7) ) // whoami
			{
				IRC_send_message(hostd_tmp.nick);
			}
			if ( !strncmp(msg, "!commands",9) ) // display available commands
			{
				IRC_send_message("Available commands: !op, !deop, !voice, !devoice, !ban, !unban, !kick, !kb, !bk, !whoami, !version, !commands.");
			}
			if ( !strncmp(msg, "!quit",9) ) // quit
			{
				fprintf(dataout, "QUIT\r\n");
				fflush(dataout);
			}
		} // channel event parsing END
	}
}

void IRC_parse_reply(char* data)
{
	char* hostd;
	char* cmd;
	char* params;
	char buffer[514];
	irc_reply_data hostd_tmp;
	char* p;
	hostd_tmp.target=0;
	if (data[0]==':') // split data (origin, message, type etc.)
	{
		hostd=&data[1];
		cmd=strchr(hostd, ' ');
		if (!cmd)
			return;
		*cmd='\0';
		cmd++;
		params=strchr(cmd, ' ');
		if (params)
		{
			*params='\0';
			params++;
		}
		hostd_tmp.nick=hostd;
		hostd_tmp.ident=strchr(hostd, '!');
		if (hostd_tmp.ident)
		{
			*hostd_tmp.ident='\0';
			hostd_tmp.ident++;
			hostd_tmp.host=strchr(hostd_tmp.ident, '@');
			if (hostd_tmp.host)
			{
				*hostd_tmp.host='\0';
				hostd_tmp.host++;
			}
		}
		IRC_action(hostd,cmd,params,hostd_tmp);
	}
	else
	{
		cmd=data;
		data=strchr(cmd, ' ');
		if (!data)
			return;
		*data='\0';
		params=data+1;
		if (!strcmp(cmd, "PING")) // PING - PONG :P
		{
			if (!params)
				return;
			fprintf(dataout, "PONG %s\r\n", &params[1]);
			fflush(dataout);
		}
	}
}

void IRC_buffer_split(char* data)
{
	char* p;
	while (p=strstr(data, "\r\n"))
	{
		*p='\0';
		IRC_parse_reply(data); // send line to parsing
		data=p+2;
	}
}

int IRC_loop()
{
	char buffer[2048];
	int ret_len;
	while (1)
	{
		ret_len=recv(irc_sock, buffer, 2047, 0); // get data from socket
		if (ret_len==-1 || !ret_len)
			return 1;
		buffer[ret_len]='\0';
		IRC_buffer_split(buffer); // split buffer to lines
	}
	return 0;
}

int main()
{
	IRC_connect();
	IRC_loop();
	return 0;
}
